Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various bug fixes. #163

Merged
merged 22 commits into from
Mar 5, 2025
Merged

Various bug fixes. #163

merged 22 commits into from
Mar 5, 2025

Conversation

michaelcfanning
Copy link
Member

  • BRK: Eliminate SEC000/101.Unclassified32CharacterString as noisy and not useful.
  • BRK: Rename SEC101/102.AdoPat friendly name to AdoLegacyPat.
  • BUG: Correct SEC000/002.Unclassified16ByteHexadecimalString id and rule name on calling GetMatchIdAndName (where SEC000/001.Unclassified64ByteBase64String was returned incorrectly before).
  • BUG: Resolve System.FormatException: The input is not a valid Base-46 string errors calling SEC101/102.AdoPat.GetMatchIdAndName by swallowing correct exception kind ArgumentException in IsChecksumValid helper.

I wish we could all be in a room together where I would invite everyone to hurl coffee cups, peanut shells, or anything else handy at my head. I completely failed to test all the code paths I strongly asserted I was covering in the last PR. Furthermore, Ross' confusion at a very strange set of conditions in a test I authored proved to be entirely warranted: these conditions indicate other bugs I simply wasn't seeing.

I am only human. Festina lente. I should have it tattooed in reverse on my forehead so that it's the first thing I see in the mirror every morning.

/// The generated key is a 32-character string that contains alphanumeric characters
/// as well as symbols from the set: .=\-:[_@\*]+?
/// </summary>
public Unclassified32CharacterString()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclassified32CharacterString

Problem #1 in the strange test code paths Ross found suspicious: the test data generated by this rule was not in strict conformance across all its logic.

Basically, we have no general testing of the UnclassifiedPotentialSecurityKeys patterns list, which I could go slam into some other bucketing test. I am deferring that work for now.

For now, I'm ripping this rule out because it's noisy and not useful. The intent here was to capture an ancient variation of an generated AAD SPN secret, then the rule was renamed as a generic Unclassified32CharacterString because it fired too frequently and really it should have been deleted entirely. If there's an analysis here, it will argue its way back in.

@@ -22,7 +22,7 @@ public Unclassified16ByteHexadecimalString()
DetectionMetadata = DetectionMetadata.HighEntropy | DetectionMetadata.Unclassified | DetectionMetadata.LowConfidence;
}

public override Tuple<string, string>? GetMatchIdAndName(string match) => new Tuple<string, string>("SEC000/001", "Unclassified64ByteBase64String");
public override Tuple<string, string>? GetMatchIdAndName(string match) => new Tuple<string, string>("SEC000/002", nameof(Unclassified16ByteHexadecimalString));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

002

boo. cut and paste error. I added invariant tests that ensure no rules collide with each other in core metadata expression and also that GetMatchIdAndName results always match the declared rule id and name.

This is what we get for being 'cute' and allowing a single rule to be flexible in what ids it covers. That's useful but it tortures the design more than a little and leaves us vulnerable to bugs of this kind.

@@ -5,14 +5,14 @@

namespace Microsoft.Security.Utilities
{
public class AdoPat : RegexPattern
public class AdoLegacyPat : RegexPattern
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AdoLegacyPat

This rule no longer applies to the latest ADO PAT format and thefore I'm renaming it.

public class AdoLegacyPatTests
{
[TestMethod]
public void AdoLegacyPat_InvalidBase32Input()
Copy link
Member Author

@michaelcfanning michaelcfanning Mar 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AdoLegacyPat_InvalidBase32Input

Boo. For all my talk of test-driven development in the last PR, I ended up not shipping a correct fix! The reason? Late-breaking changes to a test I authored altered the code coverage and then at the very end a conversion from generated exception type (from FormatException to ArgumentException) left the code in a crashing condition,

Sigh. Even while waiting on a good unit test pattern, I should have started with simple, atomic tests to reveal bugs and prove they are fixed. Duly chastened, I return here with this review. #Resolved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh. Even while waiting on a good unit test pattern, I should have started with simple, atomic tests to reveal bugs and prove they are fixed. Duly chastened, I return here with this review.

❤️

nguerrera
nguerrera previously approved these changes Mar 3, 2025
nguerrera
nguerrera previously approved these changes Mar 3, 2025
rwoll
rwoll previously requested changes Mar 4, 2025
Copy link
Member

@rwoll rwoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invariant test for no duplicate ids looks like it's missing a key piece. See comments.

Otherwise, just some nits.

public class AdoLegacyPatTests
{
[TestMethod]
public void AdoLegacyPat_InvalidBase32Input()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh. Even while waiting on a good unit test pattern, I should have started with simple, atomic tests to reveal bugs and prove they are fixed. Duly chastened, I return here with this review.

❤️

var classifier = new AdoLegacyPat();
string invalidInput = "=22222222222222222222222222";
var result = classifier.GetMatchIdAndName(invalidInput);
Assert.IsNull(result);
Copy link
Member

@rwoll rwoll Mar 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay! This is a very easy to understand test <3.

For due diligence, if you have not already, run this test attached with a debugger and ensure the null is resulting from the expected error line getting hit. i.e. this is null for a particular reason, and not a general indeterminate reason.

Depending on perf constraints, there's more formal methods to handle this kinda testing — e.g. internal logging and asserting on specific logs are hit even if everything publicly is turned into a null. Out of scope for this PR, of course, but food for thought. #Resolved

Comment on lines 36 to 37
HashSet<string> ruleIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
HashSet<string> ruleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
Copy link
Member

@rwoll rwoll Mar 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
HashSet<string> ruleIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
HashSet<string> ruleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
HashSet<string> seenRuleIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
HashSet<string> seenRuleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

nit: these renames will make the loop code easier to read #Resolved

result = ruleIds.Contains(pattern.Id);
result.Should().BeFalse(because: $"Pattern '{pattern.GetType().Name}' should not share its Id with another rule: '{pattern.Id}'");

result = ruleNames.Contains(pattern.Name);
Copy link
Member

@rwoll rwoll Mar 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
result = ruleNames.Contains(pattern.Name);
bool duplicatePatternName = ruleNames.Contains(pattern.Name);

nit: instead of using generic result everywhere some more specific names will increase readability. #WontFix

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't do this. I set result to my test condition so that it's on one line. Then I follow it with an assertion with a strong readable message.

result isn't intended to encode something useful, the detailed, highly authored message it.

i could allocate new variables with names but this tends to consume more space and i worry about readability.

let's punt for now i will think about your suggestion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read the code top to bottom. A well-named variable means I know how to read/check the line without the out-of-order flow.

Better yet if you want the because to be the sole communicator, skip using the variable altogether:

alreadySeeanRullName.Should().Not().Contains(pattern.Name, because: "…");

These are nits, so keep thinking on it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a good suggestion to encode more information about the test expectation in the variable name, I will keep this in mind moving forward.

@rwoll rwoll self-requested a review March 4, 2025 01:40
@rwoll rwoll dismissed their stale review March 4, 2025 01:41

Dismissing as not to block as bug is in unrelated test code.

nguerrera
nguerrera previously approved these changes Mar 5, 2025
@michaelcfanning michaelcfanning merged commit fee7a62 into main Mar 5, 2025
12 checks passed
@michaelcfanning michaelcfanning deleted the ado-pat-exception branch March 5, 2025 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants